/*
  Copyright (C) 2013 Napspirit / Thierry Rochelet

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


package com.napspirit.tmx.data;

import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Vector;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.Text;


/**
 * A TMX Translation Unit
 */
public class TMXTU 
    implements Comparable {
    
    /** tu::@tuid */
    protected String id;
    
    /** tu->note */
    protected String note;

    /** tu->prop::@type=="reference" */
    protected Vector<String> references;

    /** tu->prop::@type=="category" */
    protected String category;
    
    /** tuv (Translation Unit Variant) */
    protected Hashtable<Locale,String> tuvs;

    /** Main TMX */
    private TMX parent;

    /** Modified of not ? */
    protected boolean modified;

    protected TMXTU (TMX parent) {
        this.parent = parent;
        this.tuvs = new Hashtable<Locale,String>();
        this.id = "** new **";
        this.note = "";
        this.category = "";
        this.references = new Vector<String>();
        this.modified = true; // default is modified !!
        this.parent.modified();
    }

    public boolean isModified () { return this.modified; }

    protected void duplicate(TMX tmx) {        
        TMXTU tu = new TMXTU(tmx);
        tmx.addTU(tu);

        tu.setID(this.id);
        tu.setNote(this.note);
        tu.modified = true;
        for (String ref : this.references) {
            tu.addReference(ref);
        }
        
        Enumeration<Locale> locales = this.tuvs.keys();
        while (locales.hasMoreElements()) {
            Locale locale = locales.nextElement();
            tu.setLocale(locale,this.getLocale(locale));
        }
    }

    // ===== LOCALE =====

    public void registerLocale (Locale locale) {
        // already defined ?
        if (this.tuvs.containsKey(locale)) {
            return;
        }

        // Default definition if the ID
        this.tuvs.put(locale,this.id);

        // Test onits parent
        this.parent.registerLocale(locale);
      
        this.modified = true;
        this.parent.modified();
    }

    public void setLocale (Locale locale,String val) {
        if (!this.tuvs.containsKey(locale)) {
            this.registerLocale(locale);
        }

        String old = this.tuvs.get(locale);
        if (val.compareTo(old)==0) {
            return; // nothing todo
        }
        else {
            this.tuvs.put(locale,val);
            this.modified = true;
            this.parent.modified();
        }

    }

    public Enumeration<Locale> getLocales () { return this.tuvs.keys(); }

    public String getLocale(Locale locale) { return this.tuvs.get(locale); }

    public boolean isLocaleDefined (Locale locale) {
        return this.tuvs.containsKey(locale);
    }
    


    // ===== ID =====

    public String getID () { return this.id; }
    public void setID (String id) { 
        if (id.compareTo(this.id)==0) {
            return; // nothing todo
        }

        this.id = id; 
        this.modified = true;
        this.parent.modified();
    }

    public boolean similarID (String id) {
        if (id==null) {
            return false;
        }

        if (this.id.compareTo(id)==0) {
            return true;
        }
        return false;
    }

    // ===== Note =====

    public String getNote () { return this.note; }

    public void  setNote (String note) { 
        if (note==null) {
            note = "";
        }

        if (note.compareTo(this.note)==0) {
            return; // nothing todo
        }

        this.note = note; 
        this.modified = true;
        this.parent.modified();
    }

    public void appendNote (String note) {
        if (note==null) {
            return;
        }
        note = note.trim();
        if (note.isEmpty()) {
            return;
        }

        if (this.note.isEmpty()) {
            this.setNote(note);
        }
        else {
            this.setNote(this.note+"\n"+note);
        }
    }

    // ===== Reference ====

    public boolean hasReferences () {
        if (this.references.size()==0) {
            return false;
        }
        return true;
    }

    public Enumeration<String> getReferences () { return this.references.elements(); }

    public void resetReferences () {
        this.references = new Vector<String>();
        this.modified = true;
        this.parent.modified();        
    }

    public void addReference (String reference) {
        if (reference==null) {
            return;
        }
        reference = reference.trim();
        if (reference.isEmpty()) {
            return;
        }

        for (String ref : this.references) {
            if (ref.compareTo(reference)==0) {
                return; // already defined
            }
        }
        this.references.add(reference);
        this.modified = true;
        this.parent.modified();
    }

    // ===== Category ====

    public String getCategory () { return this.category; }

    public void setCategory (String category) {
        if (category==null) {
            category = "";
        }

        category = category.trim().toLowerCase();

        if (category.compareTo(this.category)==0) {
            return; // nothing todo
        }
       
        this.category = category; 
        this.parent.registerCategory(this.category);
        this.modified = true;
        this.parent.modified();        
    }


    // ==== READ / LOAD ====

    protected static void loadXML (TMX tmx,Node node) {
        TMXTU tu = new TMXTU(tmx);

        if (node.getAttributes().getNamedItem("tuid")!=null) {
            String __tuid = node.getAttributes().getNamedItem("tuid").getNodeValue();
            tu.id = __tuid;
        }

        // -> notes*
        // -> tuv*
        NodeList subs = node.getChildNodes();
        for(int i=0;i<subs.getLength();i++) {
            Node sub = subs.item(i);
            if(sub.getNodeType()==Node.ELEMENT_NODE)

                if(sub.getNodeName().compareTo("tuv")==0) {
                    // ->tuv*
                    Node tuv = sub;
                    // ->tuv:: @xml:lang
                    String __lang = tuv.getAttributes().getNamedItem("xml:lang").getNodeValue();
                    Locale locale = new Locale(__lang);
                    tu.registerLocale(locale);

                    // ->tuv->seg
                    String __value = "";
                    NodeList segs = tuv.getChildNodes();
                    for(int j=0;j<segs.getLength();j++) {
                        Node seg = segs.item(j);
                        if(seg.getNodeType()==Node.ELEMENT_NODE)
                            if(seg.getNodeName().compareTo("seg")==0) {
                                if (seg.getFirstChild()!=null) {
                                    __value = seg.getFirstChild().getNodeValue();
                                }
                            }
                    }
                    tu.setLocale(locale,__value);
                }

            
                else if(sub.getNodeName().compareTo("note")==0) {
                    // ->note*
                    if (sub.getFirstChild()!=null) {
                        String _note = sub.getFirstChild().getNodeValue().trim();
                        tu.appendNote(_note);
                    }
                }

                else if(sub.getNodeName().compareTo("prop")==0) {
                    // ->prop*
                    String _type = sub.getAttributes().getNamedItem("type").getNodeValue();
                    String _content = null;
                    if (sub.getFirstChild()!=null) {
                        _content = sub.getFirstChild().getNodeValue().trim();
                        if (_content.isEmpty()) {
                            _content = null;
                        }
                    }
                    
                    if(_type.compareTo("reference")==0 && _content!=null) {
                        tu.addReference(_content);
                    }
                    else if (_type.compareTo("category")==0 && _content!=null) {
                        tu.setCategory(_content);
                    }
                }            
        }
        
        tu.modified = false;
        tmx.addTU(tu);
    }


    // ==== WRITE ====

    protected void writeXML (Document document,Element body) {
        Element _tu = document.createElement("tu");
        body.appendChild(_tu);

        _tu.setAttribute("tuid",this.id);
        _tu.setAttribute("datatype","PlainText");

        // tu->note
        if (!this.note.isEmpty()) {
            Element _note = document.createElement("note");
            _tu.appendChild(_note);
            Text _text = document.createTextNode(this.note);
            _note.appendChild(_text);
        }

        // tu->prop reference
        if (this.hasReferences()) {
            Element _prop = document.createElement("prop");
            _tu.appendChild(_prop);
            _prop.setAttribute("type","reference");
            for (String ref : this.references) {
                Text _text = document.createTextNode(ref);
                _prop.appendChild(_text);
                
            }
        }

        // tu->prop category
        if (!this.category.isEmpty()) {
            Element _prop = document.createElement("prop");
            _tu.appendChild(_prop);
            _prop.setAttribute("type","category");
            Text _text = document.createTextNode(this.category);
            _prop.appendChild(_text);
        }


        
        Enumeration<Locale> locales = this.tuvs.keys(); 
        while(locales.hasMoreElements()) {            
            Locale locale = locales.nextElement();
            String val = this.tuvs.get(locale);
            if(val!=null) {
                Element _tuv = document.createElement("tuv");
                _tu.appendChild(_tuv);
                _tuv.setAttribute("xml:lang",TMX.getRFC3066(locale));
		    
                Element _seg = document.createElement("seg");
                _tuv.appendChild(_seg);

                Text _text = document.createTextNode(val);
                _seg.appendChild(_text);
            }
	}
    }

    public int compareTo (Object obj) {
        try {
            TMXTU tu = (TMXTU)obj;
            return this.id.compareTo(tu.id);
        } catch (Exception e) {
            return -1;
        }
    }


    // =========== LOCALES =============

    public String [] getNotDefinedLocales() {        
        String [] iso = Locale.getISOLanguages();
        Vector<String> iso2 = new Vector<String>();

        for (int i=0;i<iso.length;i++) {
            if (!this.tuvs.containsKey(new Locale(iso[i]))) {
                iso2.add(iso[i]);
            }
        }

        String [] tmp = iso2.toArray(new String[iso2.size()]);
        return tmp;
    }

    public String [] getDefinedLocales() {        
        String [] iso = Locale.getISOLanguages();
        Vector<String> iso2 = new Vector<String>();

        for (int i=0;i<iso.length;i++) {
            if (this.tuvs.containsKey(new Locale(iso[i]))) {
                iso2.add(iso[i]);
            }
        }

        String [] tmp = iso2.toArray(new String[iso2.size()]);
        return tmp;
    }


    // ======= IMPORT ======

    protected String importTUV (TMXTU tu,boolean test) {
        StringBuffer buf = new StringBuffer();
        
        Enumeration<Locale> locales = tu.tuvs.keys();
        while (locales.hasMoreElements()) {
            Locale locale = locales.nextElement();
            if (!this.isLocaleDefined(locale)) {
                if (!test) {
                    this.setLocale(locale,tu.getLocale(locale));
                }
                buf.append("<li>Import TUV on TU ["+this.getID()+"] with language : "+locale.getLanguage()+"</li>");
            }
        }        
        return buf.toString();
    }

}